home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectPlay / Maze / MazeCommon / Maze.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-31  |  11.9 KB  |  396 lines

  1. //----------------------------------------------------------------------------
  2. // File: maze.cpp
  3. //
  4. // Desc: see main.cpp
  5. //
  6. // Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
  7. //-----------------------------------------------------------------------------
  8. #define STRICT
  9. #define D3D_OVERLOADS
  10. #include <windows.h>
  11. #include <d3dx.h>
  12. #include <stdio.h>
  13. #include <math.h>
  14. #include <malloc.h>
  15. #include <dplay8.h>
  16. #include <dpaddr.h>
  17. #include <dxerr8.h>
  18. #include "DXUtil.h"
  19. #include "Maze.h"
  20. #include "MazeServer.h"
  21.  
  22.  
  23.  
  24. //-----------------------------------------------------------------------------
  25. // Name: 
  26. // Desc: 
  27. //-----------------------------------------------------------------------------
  28. CMaze::CMaze()
  29. {
  30.     m_dwWidth = m_dwHeight = m_dwSeed = m_dwSize = 0;
  31.     m_pMaze = NULL;
  32.     m_dwMaxView = 15;
  33. }
  34.  
  35.  
  36.  
  37.  
  38. //-----------------------------------------------------------------------------
  39. // Name: 
  40. // Desc: 
  41. //-----------------------------------------------------------------------------
  42. CMaze::~CMaze()
  43. {
  44.     if( m_pMaze != NULL )
  45.         DXTRACE( TEXT("Warning: Destructing CMaze object without calling Empty()\n") );
  46.  
  47.     Empty();
  48. }
  49.  
  50.  
  51.  
  52.  
  53. //-----------------------------------------------------------------------------
  54. // Name: 
  55. // Desc: 
  56. //-----------------------------------------------------------------------------
  57. HRESULT CMaze::Init( DWORD dwWidth, DWORD dwHeight, DWORD dwSeed )
  58. {
  59.     HRESULT hr;
  60.     CellNode* pCells = NULL;
  61.     CellNode* pCellNode = NULL;
  62.     DWORD dwNumWalls;
  63.     WallNode* pWalls = NULL;
  64.     WallNode* pWallNode = NULL;
  65.     WallNode tempWall;
  66.     DWORD    dwIndex;
  67.     DWORD i;
  68.  
  69.     m_Random.Reset( dwSeed );
  70.  
  71.     // Empty out any old data
  72.     Empty();
  73.  
  74.     // Store parameters and compute number of cells in the maze
  75.     m_dwWidth  = dwWidth;
  76.     m_dwHeight = dwHeight;
  77.     m_dwSeed   = dwSeed;
  78.     m_dwSize   = m_dwWidth * m_dwHeight;
  79.  
  80.     // Must be non-zero
  81.     if( m_dwSize == 0 )
  82.     {
  83.         hr = E_INVALIDARG;
  84.         DXTRACE_ERR( TEXT("Maze height and width need must be greater than 0"), E_INVALIDARG );
  85.         goto LFail;
  86.     }
  87.  
  88.     // Validate maze size
  89.     if( m_dwWidth > SERVER_MAX_WIDTH || m_dwHeight > SERVER_MAX_HEIGHT )
  90.     {
  91.         hr = E_INVALIDARG;
  92.         DXTRACE_ERR( TEXT("Maze height and width must be less than 128"), E_INVALIDARG );
  93.         goto LFail;
  94.     }
  95.  
  96.     if( (m_dwWidth % LOCK_GRID_SIZE) != 0 || (m_dwHeight % LOCK_GRID_SIZE) != 0 )
  97.     {
  98.         hr = E_INVALIDARG;
  99.         DXTRACE_ERR( TEXT("Maze height and width need to be divisable by 16"), E_INVALIDARG );
  100.         goto LFail;
  101.     }
  102.  
  103.     // Allocate maze, and initially make all walls solid
  104.     m_pMaze = new BYTE[m_dwSize];
  105.     if( m_pMaze == NULL )
  106.     {
  107.         hr = E_OUTOFMEMORY;
  108.         DXTRACE_ERR( TEXT("new"), hr );
  109.         goto LFail;
  110.     }
  111.     memset( m_pMaze, MAZE_WALL_ALL, m_dwSize );
  112.  
  113.     // Okay, now we're going to generate the maze. We use Kruskal's algorithm, which
  114.     // works by walking through the list of walls in a random order, removing a wall
  115.     // if it would connect two previously (path-)disconnected cells. This guarantees
  116.     // a fully connected maze (i.e. you can reach any cell from any other).
  117.  
  118.     // Allocate and initialize temporary cell list
  119.     pCells = new CellNode[m_dwSize];
  120.     if( pCells == NULL )
  121.     {
  122.         hr = E_OUTOFMEMORY;
  123.         DXTRACE_ERR( TEXT("new"), hr );
  124.         goto LFail;
  125.     }
  126.  
  127.     pCellNode = pCells;
  128.     for( i = 0; i < m_dwSize; i++ )
  129.     {
  130.         pCellNode->pNext = NULL;
  131.         pCellNode->pPartition = pCellNode;
  132.         pCellNode++;
  133.     }
  134.  
  135.     // Create list of walls
  136.     dwNumWalls = ((m_dwWidth-1)*m_dwHeight)+((m_dwHeight-1)*m_dwWidth);
  137.     pWalls = new WallNode[dwNumWalls];
  138.     if( pWalls == NULL )
  139.     {
  140.         hr = E_OUTOFMEMORY;
  141.         DXTRACE_ERR( TEXT("new"), hr );
  142.         goto LFail;
  143.     }
  144.  
  145.     pWallNode = pWalls;
  146.     for( i = 1; i < m_dwWidth; i++ )
  147.     {
  148.         for( DWORD j = 0; j < m_dwHeight; j++, pWallNode++ )
  149.         {
  150.             pWallNode->dwX = i;
  151.             pWallNode->dwY = j;
  152.             pWallNode->dwType = MAZE_WALL_WEST;
  153.         }
  154.     }
  155.     for( i = 0; i < m_dwWidth; i++ )
  156.     {
  157.         for( DWORD j = 1; j < m_dwHeight; j++, pWallNode++ )
  158.         {
  159.             pWallNode->dwX = i;
  160.             pWallNode->dwY = j;
  161.             pWallNode->dwType = MAZE_WALL_NORTH;
  162.         }
  163.     }
  164.  
  165.     // Randomly permute the wall list
  166.     for( i = dwNumWalls-1; i > 0; i-- )
  167.     {
  168.         dwIndex         = m_Random.Get(i);
  169.         tempWall        = pWalls[dwIndex];
  170.         pWalls[dwIndex] = pWalls[i];
  171.         pWalls[i]       = tempWall;
  172.     }
  173.  
  174.     // Walk through all the walls
  175.     pWallNode = pWalls;
  176.     for( i = 0; i < dwNumWalls; i++, pWallNode++ )
  177.     {
  178.         // Determine the cells either side of the wall
  179.         DWORD dwCellA = pWallNode->dwX + (pWallNode->dwY * m_dwWidth);
  180.         DWORD dwCellB = dwCellA;
  181.         if( pWallNode->dwType == MAZE_WALL_NORTH )
  182.             dwCellB -= m_dwWidth;
  183.         else
  184.             dwCellB--;
  185.  
  186.         // Are they already connected (partitions equal)?
  187.         CellNode* pCellA = pCells+dwCellA;
  188.         CellNode* pCellB = pCells+dwCellB;
  189.         if( pCellA->pPartition != pCellB->pPartition )
  190.         {
  191.             // Nope, so let's take out that wall. First, connect the partition lists
  192.             while ( pCellA->pNext )
  193.                 pCellA = pCellA->pNext;
  194.             pCellB = pCellB->pPartition;
  195.             pCellA->pNext = pCellB;
  196.             while ( pCellB )
  197.             {
  198.                 pCellB->pPartition = pCellA->pPartition;
  199.                 pCellB = pCellB->pNext;
  200.             }
  201.  
  202.             // Now remove the walls in our maze array
  203.             if( pWallNode->dwType == MAZE_WALL_NORTH )
  204.             {
  205.                 m_pMaze[dwCellA] &= ~MAZE_WALL_NORTH;
  206.                 m_pMaze[dwCellB] &= ~MAZE_WALL_SOUTH;
  207.             }
  208.             else
  209.             {
  210.                 m_pMaze[dwCellA] &= ~MAZE_WALL_WEST;
  211.                 m_pMaze[dwCellB] &= ~MAZE_WALL_EAST;
  212.             }
  213.         }
  214.     }
  215.  
  216.     // Free temporary wall and cell lists
  217.     delete[] pWalls;
  218.     delete[] pCells;
  219.  
  220.     return S_OK;
  221.  
  222. LFail:
  223.     SAFE_DELETE_ARRAY( pCells );
  224.     SAFE_DELETE_ARRAY( pWalls );
  225.     SAFE_DELETE_ARRAY( m_pMaze );
  226.     return hr;
  227. }
  228.  
  229.  
  230.  
  231.  
  232. //-----------------------------------------------------------------------------
  233. // Name: 
  234. // Desc: 
  235. //-----------------------------------------------------------------------------
  236. void CMaze::Empty()
  237. {
  238.     if( m_pMaze != NULL )
  239.         SAFE_DELETE_ARRAY( m_pMaze );
  240.  
  241.     m_dwWidth = m_dwHeight = m_dwSeed = m_dwSize = 0;
  242. }
  243.  
  244.  
  245.  
  246.  
  247. //-----------------------------------------------------------------------------
  248. // Name: 
  249. // Desc: 
  250. //-----------------------------------------------------------------------------
  251. DWORD CMaze::GetVisibleCells( const D3DXVECTOR2& pos, const D3DXVECTOR2& dir ,
  252.                               float fov, MazeCellRef* plist, DWORD maxlist )
  253. {
  254.     // Check we have a maze, and that we were passed reasonable parameters
  255.     if( m_pMaze == NULL || plist == NULL || maxlist == 0 )
  256.         return 0;
  257.  
  258.     // Check bounds of given viewpoint, must be inside maze
  259.     if( pos.x < 0.0f || pos.y < 0.0f || 
  260.         pos.x >= float(m_dwWidth) || pos.y >= float(m_dwHeight) )
  261.         return 0;
  262.  
  263.     // State data for the algorithm
  264.     VisState state;
  265.  
  266.     // Figure out which cell the viewpoint is in
  267.     state.dwPosX = DWORD(pos.x);
  268.     state.dwPosY = DWORD(pos.y);
  269.     state.vPos   = pos;
  270.  
  271.     // Compute view boundaries
  272.     float   c = float(cos(fov*0.5f));
  273.     float   s = float(sin(fov*0.5f));
  274.     D3DXVECTOR2 left,right;
  275.     left.x  = (dir.x*c)+(dir.y*s);
  276.     left.y  = (dir.y*c)-(dir.x*s);
  277.     right.x = (dir.x*c)-(dir.y*s);
  278.     right.y = (dir.y*c)+(dir.x*s);
  279.  
  280.     // Store view direction (for near plane clip)
  281.     state.vDir = dir;
  282.  
  283.     // Figure out boundary of area we're prepared to look at (view cutoff)
  284.     state.dwMinX = (state.dwPosX > m_dwMaxView) ? state.dwPosX - m_dwMaxView : 0;
  285.     state.dwMaxX = ((state.dwPosX + m_dwMaxView) > m_dwWidth) ? m_dwWidth : state.dwPosX + m_dwMaxView;
  286.     state.dwMinY = (state.dwPosY > m_dwMaxView) ? state.dwPosY - m_dwMaxView : 0;
  287.     state.dwMaxY = ((state.dwPosY + m_dwMaxView) > m_dwHeight) ? m_dwHeight : state.dwPosY + m_dwMaxView;
  288.     state.dwArrayPitch = state.dwMaxX-state.dwMinX+1;
  289.  
  290.     // Allocate a temporary buffer which we'll use to mark visited cells
  291.     DWORD array_size = state.dwArrayPitch * (state.dwMaxY-state.dwMinY+1);
  292.     state.pArray = (BYTE*)_alloca( array_size );
  293.     ZeroMemory( state.pArray, array_size );
  294.  
  295.     state.ppVisList = &plist;
  296.     state.dwMaxList = maxlist;
  297.     state.dwListLen = 0;
  298.  
  299.     // Recurse through cells
  300.     RecurseCheckCellVis( state, state.dwPosX, state.dwPosY, left, right );
  301.  
  302.     return state.dwListLen;
  303. }
  304.  
  305.  
  306.  
  307.  
  308. //-----------------------------------------------------------------------------
  309. // Name: 
  310. // Desc: 
  311. //-----------------------------------------------------------------------------
  312. void CMaze::RecurseCheckCellVis( VisState& state, DWORD x, DWORD y, 
  313.                                  D3DXVECTOR2 left, D3DXVECTOR2 right )
  314. {
  315.     // Fall out if we've overrun list length
  316.     if( state.dwListLen >= state.dwMaxList )
  317.         return;
  318.  
  319.     // If cell is outside the maximum view bounds, then it's not visible
  320.     if( x < state.dwMinX || x > state.dwMaxX || 
  321.         y < state.dwMinY || y > state.dwMaxY )
  322.         return;
  323.  
  324.     // If cell is already marked, then we don't visit it either
  325.     if( state.pArray[x-state.dwMinX+((y-state.dwMinY)*state.dwArrayPitch)] )
  326.         return;
  327.  
  328.     // Mark cell as visited
  329.     state.pArray[x-state.dwMinX+((y-state.dwMinY)*state.dwArrayPitch)] = 1;
  330.  
  331.     // Compute visibility flags
  332.     D3DXVECTOR2 offset;
  333.     offset.x = float(x)-state.vPos.x;
  334.     offset.y = float(y)-state.vPos.y;
  335.     BYTE   flags[4];
  336.     flags[0] = ComputeVisFlags( state.vDir, left, right, offset );
  337.     offset.x += 1.0f;
  338.     flags[1] = ComputeVisFlags( state.vDir, left, right, offset );
  339.     offset.y += 1.0f;
  340.     flags[2] = ComputeVisFlags( state.vDir, left, right, offset );
  341.     offset.x -= 1.0f;
  342.     flags[3] = ComputeVisFlags( state.vDir, left, right, offset );
  343.     offset.y -= 1.0f;
  344.  
  345.     // If there is an edge which clips all points, then the cell isn't in frustrum
  346.     if( flags[0]&flags[1]&flags[2]&flags[3] )
  347.         return;
  348.  
  349.     // Cell is visible, so add it to list
  350.     (*state.ppVisList)->x = x;
  351.     (*state.ppVisList)->y = y;
  352.     (*state.ppVisList)++;
  353.     state.dwListLen++;
  354.  
  355.     // Recurse into adjoining cells. Can move into an adjacent cell only if 
  356.     // there is a 'portal' (i.e. hole in the wall) that is not clipped and 
  357.     // that lies on the correct side of the viewport.
  358.     BYTE   cell = GetCell(x,y);
  359.     D3DXVECTOR2 se = offset + D3DXVECTOR2(1,1);
  360.  
  361.     if( !(cell & MAZE_WALL_NORTH) && offset.y < 0 && !(flags[0]&flags[1]) )
  362.         RecurseCheckCellVis( state, x, y-1, left, right );
  363.  
  364.     if( !(cell & MAZE_WALL_SOUTH) && se.y > 0 && !(flags[2]&flags[3]) )
  365.         RecurseCheckCellVis( state, x, y+1, left, right );
  366.  
  367.     if( !(cell & MAZE_WALL_WEST) && offset.x < 0 && !(flags[3]&flags[0]) )
  368.         RecurseCheckCellVis( state, x-1, y, left, right );
  369.  
  370.     if( !(cell & MAZE_WALL_EAST) && se.x > 0 && !(flags[1]&flags[2]) )
  371.         RecurseCheckCellVis( state, x+1, y, left, right );
  372.  
  373.     return;
  374. }
  375.  
  376.  
  377.  
  378.  
  379. //-----------------------------------------------------------------------------
  380. // Name: 
  381. // Desc: 
  382. //-----------------------------------------------------------------------------
  383. BYTE CMaze::ComputeVisFlags( const D3DXVECTOR2& dir, const D3DXVECTOR2& left, 
  384.                              const D3DXVECTOR2& right, const D3DXVECTOR2& offset )
  385. {
  386.     BYTE flag = (D3DXVec2Dot(&offset, &dir) >= 0) ? 0 : 1;
  387.  
  388.     if( D3DXVec2CCW(&offset,&left) > 0 )
  389.         flag |= 2;
  390.  
  391.     if( D3DXVec2CCW(&offset,&right) < 0 )
  392.         flag |= 4;
  393.  
  394.     return flag;
  395. }
  396.